﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;

namespace zijian666.Http;

/// <summary>
/// 链式调用 <seealso cref="HttpClient"/>
/// </summary>
public readonly struct FluentHttpClient : IDisposable, IFormattable
{
    // http method 映射
    private static readonly Dictionary<string, HttpMethod> _methods = new(StringComparer.OrdinalIgnoreCase)
    {
        ["Get"] = HttpMethod.Get,
        ["Post"] = HttpMethod.Post,
        ["Delete"] = HttpMethod.Delete,
        ["Head"] = HttpMethod.Head,
        ["Options"] = HttpMethod.Options,
        ["Trace"] = HttpMethod.Trace,
        ["Put"] = HttpMethod.Put,
        ["Patch"] = new HttpMethod("PATCH"),
    };

    private const string CRLF = "\r\n";
    private readonly HttpMessageInvoker _httpMessageInvoker;
    private readonly Uri _baseUrl;
    private readonly string _path;
    private readonly string _hash;
    private readonly string _method;
    private readonly string _queryString;
    private readonly HttpContent _content;
    private readonly SinglyLinkedHeader? _headers;

    /// <summary>
    /// 默认 User-Agent 信息
    /// </summary>
    public static string GlobalUserAgent { get; set; } = ((Func<string>)delegate
    {
        var windows = Environment.OSVersion.ToString();
        if (Environment.Is64BitOperatingSystem)
        {
            windows += "; WOW64";
        }
        var nf = ".NET-Framework/" + Environment.Version;
        var name = typeof(HttpClientExtensions).Assembly.GetName();
        return $"{nf} ({windows}) {name.Name}/{name.Version} ({Environment.MachineName}; {Environment.UserName})";
    })();
    /// <summary>
    /// 构造函数
    /// </summary>
    /// <exception cref="ArgumentNullException"><paramref name="httpMessageInvoker"/> is null</exception>
    public FluentHttpClient(HttpMessageInvoker httpMessageInvoker = null
                            , string method = null
                            , Uri url = null
                            , string path = null
                            , string queryString = null
                            , string hash = null
                            , HttpContent content = null
                            , SinglyLinkedHeader? headers = null)
    {
        _httpMessageInvoker = httpMessageInvoker ?? throw new ArgumentNullException(nameof(httpMessageInvoker));
        _method = method ?? "GET";
        _baseUrl = url;
        _path = path;
        _hash = hash;
        _queryString = queryString;
        _content = content;
        _headers = headers;
    }
    /// <summary>
    /// 设置 <see cref="_baseUrl"/>
    /// </summary>
    public FluentHttpClient BaseUrl(Uri uri = null)
        => new(_httpMessageInvoker, _method, uri, _path, _queryString, _hash, _content, _headers);
    /// <summary>
    /// 设置 <see cref="_method"/>
    /// </summary>
    public FluentHttpClient Method(string method = null)
        => new(_httpMessageInvoker, method, _baseUrl, _path, _queryString, _hash, _content, _headers);
    /// <summary>
    /// 设置 <see cref="_path"/>
    /// </summary>
    public FluentHttpClient Path(string path = null)
        => new(_httpMessageInvoker, _method, _baseUrl, path, _queryString, _hash, _content, _headers);
    /// <summary>
    /// 设置 <see cref="_queryString"/>
    /// </summary>
    public FluentHttpClient QueryString(string queryString = null, string hash = null)
        => new(_httpMessageInvoker, _method, _baseUrl, _path, queryString, hash, _content, _headers);
    /// <summary>
    /// 设置 <see cref="_hash"/>
    /// </summary>
    public FluentHttpClient QueryHash(string hash = null)
        => new(_httpMessageInvoker, _method, _baseUrl, _path, _queryString, hash, _content, _headers);
    /// <summary>
    /// 设置请求体
    /// </summary>
    public FluentHttpClient Body(HttpContent content = null)
        => new(_httpMessageInvoker, "POST", _baseUrl, _path, _queryString, _hash, content, _headers);
    /// <summary>
    /// 设置请求头
    /// </summary>
    public FluentHttpClient Header(string name = null, string value = null)
        => new(_httpMessageInvoker, _method, _baseUrl, _path, _queryString, _hash, _content, new SinglyLinkedHeader(name, value, false, _headers));
    /// <summary>
    /// 设置默认请求头
    /// </summary>
    public FluentHttpClient DefaultHeader(string name, string value = null)
        => new(_httpMessageInvoker, _method, _baseUrl, _path, _queryString, _hash, _content, new SinglyLinkedHeader(name, value, true, _headers));
    /// <summary>
    /// 获取请求的基础URL
    /// </summary>
    public Uri BaseUrl() => _baseUrl;
    /// <summary>
    /// 获取请求路径
    /// </summary>
    public string Path() => _path;
    /// <summary>
    /// 获取查询参数字符串
    /// </summary>
    public string QueryString() => _queryString;
    /// <summary>
    /// 获取请求体
    /// </summary>
    public HttpContent Body() => _content;

    /// <summary>
    /// 获取请求头
    /// </summary>
    public IEnumerable<(string name, string value)> Headers()
    {
        var list = new List<(string name, string value, bool isDefalut)>();
        var header = _headers;
        while (header is not null)
        {
            var h = header.Value;
            list.Add((h.Name, h.Value, h.IsDefalutValue));
            header = h.Prev;
        }
        for (int i = list.Count - 1; i >= 0; i--)
        {
            var (name, value, isDefalut) = list[i];
            if (isDefalut && list.Any(x => x.name == name))
            {
                continue;
            }
            yield return (name, value);
        }
    }
    // 返回请求头的可循环对象
    private IEnumerable<(string name, string value, bool isDefault)> EnumerableHeaders()
    {
        var header = _headers;
        while (header is not null)
        {
            var h = (SinglyLinkedHeader)header;
            yield return (h.Name, h.Value, h.IsDefalutValue);
            header = h.Prev;
        }
    }
    /// <summary>
    /// 异步发送请求
    /// </summary>
    public Task<HttpResponseMessage> SendAsync(CancellationToken cancellationToken)
    {
        var message = BuildRequestMessage();
        return _httpMessageInvoker.SendAsync(message, cancellationToken);
    }

    /// <summary>
    /// 构造用于描述一个http请求的 <seealso cref="HttpRequestMessage"/>
    /// </summary>
    private HttpRequestMessage BuildRequestMessage()
    {
        var url = BuildUrl();
        if (!_methods.TryGetValue(_method, out var method))
        {
            method = new HttpMethod(_method);
        }

        var message = new HttpRequestMessage(method, url);

        if (_content is not null)
        {
            message.Content = _content;
        }

        if (!message.Headers.Contains("User-Agent"))
        {
            message.Headers.TryAddWithoutValidation("User-Agent", GlobalUserAgent);
        }
        if (_headers is not null)
        {
            var headers = message.Headers;
            foreach (var (name, value) in Headers())
            {
                headers.TryAddWithoutValidation(name, value);
            }
        }
        return message;
    }
    /// <summary>
    /// 构造请求的链接
    /// </summary>
    private Uri BuildUrl()
    {

        UriBuilder builder;
        if (_baseUrl is not null)
        {
            if (_path is null)
            {
                builder = new UriBuilder(_baseUrl);
            }
            else
            {
                builder = new UriBuilder(new Uri(_baseUrl, _path));
            }
        }
        else if (string.IsNullOrWhiteSpace(_path))
        {
            throw new UriFormatException("请求地址有误");
        }
        else if (!_path.Contains("://"))
        {
            builder = new UriBuilder("http://" + _path);
        }
        else
        {
            builder = new UriBuilder(_path);
        }

        if (_queryString is not null)
        {
            builder.Query = _queryString;
        }
        if (_hash is not null)
        {
            builder.Fragment = _hash;
        }

        return builder.Uri;
    }

    /// <summary>
    /// 获取当前请求的报文
    /// </summary>
    public override string ToString() => ToString(null, null);
    /// <summary>
    /// 获取当前请求的指定信息
    /// </summary>
    /// <param name="format">"url" 或任何 <seealso cref="UriComponents"/></param>
    /// <param name="formatProvider">暂时没用</param>
    /// <returns></returns>
    public string ToString(string format, IFormatProvider formatProvider = null)
    {
        if (format is not null)
        {
            if (string.Equals(format, "url", StringComparison.OrdinalIgnoreCase))
            {
                return BuildUrl().GetComponents(UriComponents.Scheme | UriComponents.Host | UriComponents.Port | UriComponents.Path | UriComponents.Query | UriComponents.Fragment, UriFormat.UriEscaped);
            }
            if (Enum.TryParse<UriComponents>(format, true, out var components))
            {
                return BuildUrl().GetComponents(components, UriFormat.UriEscaped);
            }
        }

        using var request = BuildRequestMessage();
        var buffer = new StringBuilder();

        buffer.Append(request.Method.Method);
        buffer.Append(" ");
        buffer.Append(request.RequestUri.GetComponents(UriComponents.Path | UriComponents.Query | UriComponents.Fragment, UriFormat.UriEscaped));
        buffer.Append(" HTTP/");
        buffer.Append(request.Version);
        buffer.Append(CRLF);
        buffer.Append("host: ");
        buffer.Append(request.RequestUri.Host);
        buffer.Append(":");
        buffer.Append(request.RequestUri.Port);

        if (request.Content is not null)
        {
            foreach (var header in request.Content.Headers)
            {
                buffer.Append(CRLF);
                buffer.Append(header.Key);
                buffer.Append(": ");
                foreach (var value in header.Value)
                {
                    buffer.Append(value);
                    buffer.Append(",");
                }
                buffer.Length--;
            }
        }
        if (request.Headers is not null)
        {
            foreach (var header in request.Headers)
            {
                buffer.Append(CRLF);
                buffer.Append(header.Key);
                buffer.Append(": ");
                foreach (var value in header.Value)
                {
                    buffer.Append(value);
                    buffer.Append(",");
                }
                buffer.Length--;
            }
        }
        if (request.Content is not null)
        {
            buffer.Append(CRLF);
            buffer.Append(CRLF);
            var body = request.Content.ReadAsString();
            buffer.Append(body);
        }
        return buffer.ToString();
    }
    /// <summary>
    /// 释放 <see cref="_httpMessageInvoker"/>
    /// </summary>
    public void Dispose() => _httpMessageInvoker.Dispose();

}
